package com.example.demo.config;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.example.demo.entity.Chatroom;
import com.example.demo.repository.ChatroomRepository;
import com.example.demo.vo.ChatroomVo;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.antlr.v4.runtime.misc.MultiMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@ServerEndpoint("/chatroom/{activity_id}/{username}")
@Component
public class WebSocketServer implements ApplicationContextAware {
    private  static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    public static final Map<String, String> userMap = new ConcurrentHashMap<>();
    public static final Map<Integer, List<String>> activityMap = new ConcurrentHashMap<>();

    private ChatroomRepository chatroomRepository;
    private static ApplicationContext applicationContext;

    @OnOpen
    public void onOpen(Session session,@PathParam("username") String username,@PathParam("activity_id") Integer activity_id) {
        sessionMap.put(session.getId(), session);
        String user = URLUtil.decode(Base64.decodeStr(username));
        userMap.put(session.getId(), user);

        if(!activityMap.containsKey(activity_id)){
            activityMap.put(activity_id, new ArrayList<>());
        }
        activityMap.get(activity_id).add(session.getId());

        logger.info("[onOpen] Session={}, 用户 {} 来自活动 {} 建立连接,当前总在线人数: {} ",session.getId(), user,activity_id, sessionMap.size());
        sendAllMessage(JSONUtil.toJsonStr(Dict.create().set("users", activityMap.get(activity_id).size())), activity_id);
    }

    @OnClose
    public void onClose(Session session,@PathParam("activity_id") Integer activity_id,CloseReason closeReason) {
        try (Session ignored = sessionMap.remove(session.getId())) {
            logger.info("[onClose] Session={},活动{} 的用户 {} 断开连接, 原因：{},当前总在线人数: {} ",session.getId(),activity_id, userMap.get(session.getId()), closeReason , sessionMap.size());
            userMap.remove(session.getId());
            activityMap.get(activity_id).remove(session.getId());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            sendAllMessage(JSONUtil.toJsonStr(Dict.create().set("users", activityMap.get(activity_id).size())), activity_id);
        }
    }

    @OnMessage
    public void onMessage(String message, Session session, @PathParam("activity_id") Integer activity_id) {

        logger.info("[onMessage] 服务端收到来自[活动:{}] 的 [客户端:{}] 发来的 [消息：{}]",activity_id,session.getId(), message);
        // 存储消息到数据库
        // 解析消息
        ChatroomVo chatroomVo = JSONUtil.toBean(message, ChatroomVo.class);
        Chatroom chatroom = new Chatroom();
        BeanUtils.copyProperties(chatroomVo,chatroom);

        this.chatroomRepository = applicationContext.getBean(ChatroomRepository.class);
        Chatroom result=this.chatroomRepository.save(chatroom);
        if(result.getId()==null)
         {
            logger.error("服务端保存消息失败");
        }
        String from = userMap.get(session.getId());
        sendAllMessage(JSONUtil.toJsonStr(Dict.create().set("msg", message)),activity_id);
    }

    @OnError
    public void onError(Session session, Throwable throwable,@PathParam("activity_id") Integer activity_id) throws IOException {
        logger.error("[onError] 活动:{} 发生错误 session={}, reason={}", activity_id,session.getId(), throwable.getMessage());

        sessionMap.get(session.getId()).close(new CloseReason(CloseReason.CloseCodes.PROTOCOL_ERROR, throwable.getMessage()));
    }

    //    给其他客户端发送消息
    public void sendMessage(Session fromSession,String message) {
        sessionMap.forEach((id,session) -> {
            if(fromSession!=session){
                logger.info("[sendMessage] 服务端给客户端 [ Session：{} ]的消息 message：{}", id, message);
                try {
                    session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    logger.error("[sendMessage] 服务端发送消息给客户端失败，原因：{}", e.getMessage());
                }
            }
        });
    }

    //    给活动的所有客户端发送消息
    public void sendAllMessage(String message,Integer act_id) {
        if(activityMap.containsKey(act_id)){
            List<String> idList = activityMap.get(act_id);
            idList.forEach(id -> {
                try {
                    logger.info("[sendAllMessage] 服务端给 [活动:{}] 的所有客户端 [ Session：{} ]的消息 message：{}", act_id, id , message);
                    sessionMap.get(id).getBasicRemote().sendText(message);
                } catch (IOException e) {
                    logger.error("[sendAllMessage] 服务端发送消息给客户端失败，原因：{}", e.getMessage());
                }
            });
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        WebSocketServer.applicationContext = applicationContext;
    }
}
